/**
 * 此文件负责构建，实现系统截听服务，通过对jssip的一些基础封装，导出开箱即用的sip服务单例
 *
 * @author wujianx
 * @createdate 2020-12-3
 * jssip:3.3.1
 */
import Jssip from 'jssip';
import { JSONObject } from 'admin/schema/common';
import { reginfo } from '../admin/servers/servers';
import store from '../store';
import { setUaConfig } from 'store/root/action';

/**
 * sip UA回调事件类型
 */
export declare type SipUAEvent =
  | 'sipEvent'
  | 'registrationFailed'
  | 'connected'
  | 'disconnected'
  | 'newMessage'
  | 'registered'
  | 'unregistered'
  | 'newRTCSession';

/**
 * SIP URI是通过SIP呼叫他人的SIP地址方案。换句话说，一个SIP URL就是一个用户的SIP电话号码。
 * 它用来建立，改变和终止基于IP网络的用户间的呼叫。为了提供电话业务它还需要结合不同的标准和协议：特别是需要确保传输（RTP），与当前电话网络的信令互连，能够确保语音质量（RSVP），能够提供目录（LDAP），能够鉴权用户（RADIUS）等等。
 * SIP被描述为用来生成，修改和终结一个或多个参与者之间的会话。这些会话包括因特网多媒体会议，因特网（或任何IP网络）电话呼叫和多媒体发布。
 * 会话中的成员能够通过多播或单播联系的网络来通信。SIP支持会话描述，它允许参与者在一组兼容媒体类型上达成一致。它同时通过代理和重定向请求到用户当前位置来支持用户移动性。SIP不与任何特定的会议控制协议捆绑。
 * SIP URL如同电子邮件地址一样，按以下格式书写：
 * sip[s]:username@domain:port
 */
export class SysSip {
  /**
   * sip UA
   */
  private userAgent?: Jssip.UA;

  /**
   * 本地音频流
   */
  private localStream: MediaStream | null;

  /**
   * 截听会话实例
   */
  private currentSession: Jssip.RTCSession | null;

  /**
   * 构建UA所需的参数
   */
  public UAConfig?: Jssip.UserAgentConfiguration;

  /**
   * 生成sipUri所需要的域名
   */
  private scopeName?: string;

  private UAEventMap: Map<SipUAEvent, ((payload: any) => any)[]>;

  private audio: HTMLAudioElement;

  constructor() {
    this.localStream = null;
    this.audio = new Audio();
    this.UAEventMap = new Map();
    [
      'sipEvent',
      'registrationFailed',
      'connected',
      'disconnected',
      'newMessage',
      'registered',
      'unregistered',
      'newRTCSession',
    ].forEach(item => {
      this.bindUAEvent(item as SipUAEvent, (data: any) => {
        console.log('UAEventMap****', item, data);
      });
    });
    this.currentSession = null;
    (window as any).sip = this;
    // 方便调试jssip
    Jssip.debug('JsSIP:*');
  }

  /**
   * 等待获取相关信息拼接成配置之后再来构建userAgent，任何使用使用userAgent的public方法都应在此方法resolve后再执行
   * ex: getSysSip().waitInit().then(sipIns => sipIns.registereUA());
   */
  public waitInit(): Promise<SysSip> {
    return new Promise((resolve, reject) => {
      console.log(this.userAgent, 'waitInit***');
      if (this.userAgent) {
        resolve(this);
      } else {
        this.getUAConfig()
          .then(config => {
            console.log(config, 'UAconfig====>');
            this.userAgent = new Jssip.UA(config);
            // eslint-disable-next-line @typescript-eslint/no-this-alias
            const that = this;
            const userAgent = this.userAgent as Jssip.UA;
            const eventMap = this.UAEventMap;
            userAgent.on('sipEvent', (msg: Jssip.UserAgentSipEvent) => {
              const actions = eventMap.get('sipEvent');
              actions &&
                actions.forEach(action => {
                  action && action(msg);
                });
            });
            userAgent.on('registrationFailed', (msg: Jssip.UserAgentRegistrationFailedEvent) => {
              const actions = eventMap.get('registrationFailed');
              actions &&
                actions.forEach(action => {
                  action && action(msg);
                });
            });
            userAgent.on('connected', (msg: Jssip.UserAgentConnectedEvent) => {
              const actions = eventMap.get('connected');
              actions &&
                actions.forEach(action => {
                  action && action(msg);
                });
            });
            userAgent.on('newMessage', (msg: Jssip.UserAgentNewMessageEvent) => {
              const actions = eventMap.get('newMessage');
              actions &&
                actions.forEach(action => {
                  action && action(msg);
                });
            });
            userAgent.on('registered', (msg: Jssip.UserAgentRegisteredEvent) => {
              console.log('registered. 注册成功initSip in✅', msg);
              const actions = eventMap.get('registered');
              actions &&
                actions.forEach(action => {
                  action && action(msg);
                });
            });
            userAgent.on('unregistered', (msg: any) => {
              console.log('unregistered. 注销成功initSip out❌', msg);
              const actions = eventMap.get('unregistered');
              actions &&
                actions.forEach(action => {
                  action && action(msg);
                });
            });
            userAgent.on('newRTCSession', (msg: Jssip.UserAgentNewRtcSessionEvent) => {
              console.group('%c%s', 'color:green', 'newRTCSession. 收到来电initSip 🔔🔔🔔', msg);
              const actions = eventMap.get('newRTCSession');
              actions &&
                actions.forEach(action => {
                  action && action(msg);
                });

              // 远端发起呼叫
              if (msg.originator === 'remote') {
                console.group(
                  '%c%s',
                  'color:green',
                  '【sip】initSip-打电话 session--remote》》》》》》》》》》》》远端发起呼叫',
                  msg
                );
              } else {
                // 本地发起呼叫
                console.log('initSip-打电话 session--local');
                const outgoingSession = msg.session;
                outgoingSession.on('connecting', function () {
                  // 截听会话
                  that.currentSession = outgoingSession;
                });
              }
            });

            console.log('初始化-启动服务器连接');
            userAgent.start();
            resolve(this);
          })
          .catch(err => {
            reject();
          });
      }
    });
  }

  /**
   * 绑定UserAgent回调事件
   */
  public bindUAEvent(event: SipUAEvent, action: (payload?: any) => any): void {
    if (!event || !action) {
      return;
    }
    const events = (this.UAEventMap.get(event) as ((val: any) => void)[]) || [];
    !events.find(item => item.toString() === action.toString()) && events.push(action);
    this.UAEventMap.set(event, events);
  }

  /**
   * 删除UserAgent回调事件
   */
  public removeUAEvent(eventArr: SipUAEvent[]) {
    eventArr.forEach(item => {
      this.UAEventMap.set(item, []);
    });
    return this;
  }
  /**
   * 发送消息
   */
  public sendMsg(msg: JSONObject): void {
    const target = '';
    this.userAgent && this.userAgent.sendMessage(target, JSON.stringify(msg));
  }

  /**
   * 获取UA连接状态
   */
  public getUAConnect = () => {
    const rt = (this.userAgent as Jssip.UA).isConnected();
    console.log('UA-connected', rt);
    return rt;
  };

  /**
   * 注册UA
   */
  public registereUA(): void {
    (this.userAgent as Jssip.UA).register();
  }

  /**
   * 登出
   */
  public unRegistereUA(): void {
    (this.userAgent as Jssip.UA).unregister({
      all: true,
    });
  }

  /**
   * 挂断
   */
  public hangup(): void {
    (this.userAgent as Jssip.UA).terminateSessions({});
  }

  /**
   * 呼叫
   */
  public call(phone: string): void {
    const sipUri = this.scopeName ? `sip:${phone}@${this.scopeName}` : `sip:${phone}`;
    this.refreshSession(sipUri);
  }

  /**
   * 挂断截听
   */
  public stopAudio() {
    this.audio?.pause();
  }

  /**
   * 启动UA,建立连接
   */
  public startUA(): void {
    console.log('UA-start');
    (this.userAgent as Jssip.UA).start();
  }

  /**
   * 停止UA,断开连接
   */
  public stopUA(): void {
    console.log('UA-stop,uninit');
    (this.userAgent as Jssip.UA).stop();
  }

  /**
   * 清除fs链接
   */
  public deleteSipConnect(): void {
    console.log('deleteSipConnect===');
    this.userAgent && this.stopUA();
    this.userAgent = undefined;
    this.UAConfig = undefined;
  }

  /**
   * 获取UserAgent构造参数
   */
  private getUAConfig(): Promise<any> {
    return new Promise(async (resolve, reject) => {
      try {
        let data = (store.getState() as JSONObject).uaConfig;
        if (!data || !data.regUrl || !data.regAccount) {
          const result = await reginfo();
          data = result.data || {};
          store.dispatch(setUaConfig(data));
        }
        const { regUrl, regAccount } = data;
        // 创建Jssip与socket连接的实例
        const socket = new Jssip.WebSocketInterface(regUrl);
        resolve(
          (this.UAConfig = {
            sockets: [socket],
            register: false, // 为true，则start()后，立即自动注册。此处为点击签入后执行注册
            register_expires: 10, // 注册超时（秒），默认是600
            session_timers: true,
            uri: regAccount,
          })
        );
      } catch (error) {
        console.log('UAConfig配置信息不存在');
        reject();
      }
    });
  }

  /**
   * 刷新session
   */
  private refreshSession(sipUri: string): void {
    const eventHandlers = {
      progress: function (e: any) {
        console.log('process', e);
      },
      failed: function (e: any) {
        console.log('call failed with cause: ' + e);
      },
      ended: function (e: any) {
        console.log('call ended with cause: ' + e);
      },
      confirmed: function (e: any) {
        console.log('call confirmed');
      },
    };
    const option = {
      eventHandlers: eventHandlers,
      mediaConstraints: { audio: true },
    } as Jssip.UserAgentCallOptions;
    this.currentSession = this.userAgent && this.userAgent.call(sipUri, option);
  }
  /**播放音频流 */
  public playAudio(stream: MediaStream) {
    console.log('即将播放音频>>>MediaStream***', stream);
    if (stream) {
      const audio = this.audio;
      try {
        audio.srcObject = stream;
      } catch (error) {
        audio.src = window.URL.createObjectURL(stream as any);
      }
      audio.play();
    }
  }
}

let sysSip: SysSip;

export const getSysSip = (): SysSip => {
  return sysSip || (sysSip = new SysSip());
};
